package com.jxl.bean;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.jxl.context.SpringContextUtil;
import com.jxl.mapper.WsOfflineMessageMapper;
import jakarta.websocket.*;
import jakarta.websocket.server.PathParam;
import jakarta.websocket.server.ServerEndpoint;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.stream.Collectors;

/**
 * WebSocket核心类：整合连接管理、在线推送、离线存储、上线补发
 * 接口路径：ws://localhost:8080/websocket/{userId}
 */
@Component
@Slf4j
@ServerEndpoint("/websocket/{userId}")
public class WebSocket {
    // ====================== 1. 连接管理（复用你的原逻辑）======================
    /** 与某个客户端的连接会话 */
    private Session session;
    /** 当前连接的用户ID */
    private String userId;
    /** 存放所有在线WebSocket实例（线程安全） */
    private static final CopyOnWriteArraySet<WebSocket> webSockets = new CopyOnWriteArraySet<>();
    /** 存放用户ID与Session的映射（快速定位在线用户） */
    private static final ConcurrentHashMap<String, Session> sessionPool = new ConcurrentHashMap<>();

    // ====================== 2. 依赖注入（通过SpringContextUtil获取）======================
    // 删除直接初始化！这里只做声明
    private WsOfflineMessageMapper offlineMsgMapper;





    // ====================== 3. WebSocket生命周期方法 ======================
    /**
     * 1. 连接建立时触发：注册连接+补发离线消息
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("userId") String userId) {
        try {
            this.session = session;
            this.userId = userId;
            webSockets.add(this);
            sessionPool.put(userId, session);
            log.info("【WebSocket】新连接建立，用户ID：{}", userId);
            // 补发离线消息
            resendOfflineMessages(userId, session);
        } catch (Exception e) {
            log.error("【WebSocket】@OnOpen失败", e);
        }
    }

    /**
     * 2. 连接关闭时触发：移除连接
     */
    @OnClose
    public void onClose() {
        try {
            webSockets.remove(this);
            sessionPool.remove(this.userId);
            log.info("【WebSocket】连接断开，用户ID：{}，当前在线数：{}", userId, webSockets.size());
        } catch (Exception e) {
            log.error("【WebSocket】连接关闭失败，用户ID：{}", userId, e);
        }
    }

    /**
     * 3. 接收客户端消息时触发
     */
    @OnMessage
    public void onMessage(String message) {
        log.info("【WebSocket】收到客户端消息：{}", message);
        // 可扩展：处理客户端发送的消息（如心跳检测、业务请求等）
    }

    /**
     * 4. 连接出错时触发
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("【WebSocket】连接出错，用户ID：{}，错误原因：{}", userId, error.getMessage());
        error.printStackTrace();
    }


    // ====================== 4. 消息发送方法（整合离线逻辑）======================
    /**
     * ① 广播消息：发给所有在线用户
     */
    public void sendAllMessage(String message) {
        log.info("【WebSocket】广播消息：{}", message);
        for (WebSocket webSocket : webSockets) {
            try {
                if (webSocket.session.isOpen()) {
                    webSocket.session.getAsyncRemote().sendText(message);
                }
            } catch (Exception e) {
                log.error("【WebSocket】广播消息失败", e);
            }
        }
    }

    /**
     * ② 单点消息：发给指定用户（在线推送/离线存储）
     */
    public void sendOneMessage(String userId, String message) {
        // 确保mapper已初始化
        if (offlineMsgMapper == null) {
            offlineMsgMapper = SpringContextUtil.getBean(WsOfflineMessageMapper.class);
        }

        Session session = sessionPool.get(userId);
        if (session != null && session.isOpen()) {
            // 情况1：用户在线 → 直接推送
            try {
                log.info("【WebSocket】单点消息（在线），用户ID：{}，消息：{}", userId, message);
                session.getAsyncRemote().sendText(message);
            } catch (Exception e) {
                log.error("【WebSocket】单点消息（在线）发送失败，用户ID：{}", userId, e);
            }
        } else {
            // 情况2：用户离线 → 存入数据库
            try {
                WsOfflineMessage offlineMsg = new WsOfflineMessage();
                offlineMsg.setReceiveUserId(userId);
                offlineMsg.setMessageContent(message);
                offlineMsg.setIsRead(0); // 0-未读
                offlineMsgMapper.insert(offlineMsg);
                log.info("【WebSocket】单点消息（离线存储），用户ID：{}，消息：{}", userId, message);
            } catch (Exception e) {
                log.error("【WebSocket】单点消息（离线存储）失败，用户ID：{}", userId, e);
            }
        }
    }

    /**
     * ③ 批量消息：发给多个指定用户
     */
    public void sendMoreMessage(String[] userIds, String message) {
        for (String userId : userIds) {
            sendOneMessage(userId, message); // 复用单点消息逻辑，减少重复代码
        }
    }


    // ====================== 5. 私有工具方法 ======================
    /**
     * 补发离线消息：用户上线时，查询未读消息并推送，推送后标记为已读
     */
    private void resendOfflineMessages(String userId, Session session) {
        // 确保mapper已初始化
        if (offlineMsgMapper == null) {
            offlineMsgMapper = SpringContextUtil.getBean(WsOfflineMessageMapper.class);
        }


        // 1. 查询该用户的未读离线消息（is_read=0）
        List<WsOfflineMessage> unreadMsgList = offlineMsgMapper.selectList(
                new LambdaQueryWrapper<WsOfflineMessage>()
                        .eq(WsOfflineMessage::getReceiveUserId, userId)
                        .eq(WsOfflineMessage::getIsRead, 0)
                        .orderByAsc(WsOfflineMessage::getSendTime) // 按发送时间排序，先推旧消息
        );

        if (unreadMsgList.isEmpty()) {
            log.info("【WebSocket】用户ID：{} 无离线消息", userId);
            return;
        }

        // 2. 推送离线消息给用户
        log.info("【WebSocket】开始补发离线消息，用户ID：{}，消息数量：{}", userId, unreadMsgList.size());
        for (WsOfflineMessage msg : unreadMsgList) {
            session.getAsyncRemote().sendText(msg.getMessageContent());
        }

        // 3. 批量标记消息为已读（避免下次上线重复推送）
        List<Long> msgIds = unreadMsgList.stream()
                .map(WsOfflineMessage::getId)
                .collect(Collectors.toList());
        offlineMsgMapper.batchMarkAsRead(msgIds);
        log.info("【WebSocket】离线消息补发完成，用户ID：{}", userId);
    }
}